package com.xiashitech.agent.utils;

// import cn.hutool.core.date.DateUtil;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiashitech.agent.config.StartConfig;
import com.xiashitech.agent.proxy.hutool.DateUtil;
import com.xiashitech.agent.proxy.hutool.FileUtil;
import com.xiashitech.agent.proxy.hutool.ResourceUtil;
import com.xiashitech.agent.proxy.hutool.StrUtil;
import com.xiashitech.interfaces.agent.dto.api.Params;

import java.io.File;
import java.nio.charset.Charset;
import java.util.*;

public class XSUtil {

    public static final boolean XS_ERROR_ENABLE = Boolean.parseBoolean(System.getProperty("xs.log.error", "true"));

    public static boolean isEmpty(CharSequence str){
        return str == null || str.length() == 0;
    }

    public static boolean isNotEmpty(CharSequence str) {
        return false == isEmpty(str);
    }

    public static boolean isBlank(String str) {
        return str == null || str.trim().length() == 0;
    }

    public static boolean isNotBlank(String str) {
        return !isBlank(str);
    }

    public static String getAgentConfigBySystemId(Map<String, String> map){
        String config = null;
        String systemId = StartConfig.getParamValueFromKey(StartConfig.systemId);
        if (Objects.nonNull(map) && Objects.nonNull(systemId)){
            config = map.get(systemId);
        }
        return config;
    }

    public static String extractParamNamesFromRequest(Enumeration<String> paramNames){
        List<Params> requestParams = new ArrayList<>();
        while (paramNames.hasMoreElements()){
            requestParams.add(Params.builder().type("String").name(String.valueOf(paramNames.nextElement())).build());
        }
        return JsonUtil.convertObjectToString(requestParams);
    }

    public static String getJsonNodeValueByPath(String jsonStr, String path){
        String nodeValue = null;
        if (isNotEmpty(jsonStr) && isNotEmpty(path)){
            ObjectMapper mapper = new ObjectMapper();
            try {
                JsonNode jsonNode = mapper.readTree(jsonStr);
                List<String> pathArr = StrUtil.split(path, ".");
                for (String p : pathArr) {
                    jsonNode = jsonNode.path(p);
                }
                nodeValue = jsonNode.asText();
            } catch (JsonProcessingException e) {
                StartConfig.throwableStackPrinter(e);
            }
        }
        return nodeValue;
    }

    public static void debug(String log, Object... params){
        if (Boolean.parseBoolean(System.getProperty("xs.debug", "true"))){
            System.out.println(StrUtil.format("[XS.AGENT {}] [DEBUG] {}", DateUtil.now(), StrUtil.format(log, params)));
        }
    }

    public static void info(String log, Object... params){
        if (Boolean.parseBoolean(System.getProperty("xs.log.info", "true"))){
            System.out.println(StrUtil.format("[XS.AGENT {}] [INFO] {}", DateUtil.now(), StrUtil.format(log, params)));
        }
    }

    public static void error(String log, Object... params){
        if (Boolean.parseBoolean(System.getProperty("xs.log.error", "true"))){
            System.err.println(StrUtil.format("[XS.AGENT {}] [ERROR] {}", DateUtil.now(), StrUtil.format(log, params)));
        }
    }

    public static void warn(String log, Object... params) {
        if (XS_ERROR_ENABLE){
            System.err.println(StrUtil.format("[XS.AGENT {}] [WARN] {}", DateUtil.now(), StrUtil.format(log, params)));
        }
    }

    public static void debug(Class clazz, String msg, Object...params) {
        debug(StrUtil.format("[{}] {} - " + msg, Thread.currentThread().getName(), clazz.getName()), params);
    }

    public static void info(Class clazz, String msg) {
        info("[{}] {} - " + msg, Thread.currentThread().getName(), clazz.getName());
    }

    public static void info(Class clazz, String msg, Object...params) {
        info(StrUtil.format("[{}] {} - " + msg, Thread.currentThread().getName(), clazz.getName()), params);
    }

    public static void warn(Class clazz, String msg) {
        warn("[{}] {} - " + msg, Thread.currentThread().getName(), clazz.getName());
    }

    public static void warn(Class clazz, String msg, Object...params) {
        warn(StrUtil.format("[{}] {} - " + msg, Thread.currentThread().getName(), clazz.getName()), params);
    }

    public static void error(Class clazz, String msg, Object...params) {
        error(StrUtil.format("[{}] {} - " + msg, Thread.currentThread().getName(), clazz.getName()), params);
    }

    public static void error(Class clazz, String msg, Throwable throwable, Object...params) {
        error(StrUtil.format("[{}] {} - " + msg, Thread.currentThread().getName(), clazz.getName()), params);
        if (XS_ERROR_ENABLE) {
            throwable.printStackTrace(System.err);
        }
    }

    public static void error(Class clazz, Throwable throwable) {
        error(StrUtil.format("[{}] {} - {}", Thread.currentThread().getName(), clazz.getName()), throwable.getMessage());
    }

    public static void forceLog(String log){
        FileUtil.appendUtf8String(StrUtil.format("[{}] {} \n", DateUtil.now(), log), new File("/tmp/XS.log"));
    }

    public static String mockAgentServer(String url){
        String response = "";
        if (isEmpty(url)) return response;
        if (url.contains("/syncConfig")){
            response = ResourceUtil.readStr("mock/syncConfig.json", Charset.defaultCharset());
        } else if (url.contains("/getSystemInfo")) {
            response = ResourceUtil.readStr("mock/getSystemInfo.json", Charset.defaultCharset());
        } else if (url.contains("/syncAgentStatus")) {
            response = ResourceUtil.readStr("mock/syncAgentStatus.json", Charset.defaultCharset());
        } else if (url.contains("/syncApisToOpenMetadata")) {
            response = ResourceUtil.readStr("mock/syncApisToOpenMetadata.json", Charset.defaultCharset());
        }else if (url.contains("/agentStatus")) {
            response = ResourceUtil.readStr("mock/agentStatus.json", Charset.defaultCharset());
        } else if (url.contains("/supplementTracing")) {
            response = ResourceUtil.readStr("mock/supplementTracing.json", Charset.defaultCharset());
        } else if (url.contains("/syncAgentHost")) {
            response = ResourceUtil.readStr("mock/syncAgentHost.json", Charset.defaultCharset());
        } else if (url.contains("/heartBeat")) {
            response = ResourceUtil.readStr("mock/heartBeat.json", Charset.defaultCharset());
        }
        // too many boring calls on '/agentStatus'
        if (!url.contains("/agentStatus")){
            debug("mockAgentServer url: " + url );
        }
        return response;
    }


    // copy from JsonUtil
    // 只返回叶子节点
    public static void getJsonKeys(String json, Set<String> jsonKeys) {
        try {
            ObjectMapper jackson = new ObjectMapper();
            JsonNode node = jackson.readTree(json);
            getJsonKeys(node,jsonKeys,"");
        } catch (Throwable e) {
            StartConfig.throwableStackPrinter(e);
        }
    }
    public static void getJsonKeys(JsonNode node, Set<String> jsonKeys, String parentStr) {
        if (node.isObject()) {
            Iterator<Map.Entry<String, JsonNode>> it = node.fields();
            while (it.hasNext()) {
                Map.Entry<String, JsonNode> entry = it.next();
                if (entry.getValue() != null && !JsonUtil.isJson(entry.getValue().toString())){
                    // 跳过树枝节点
                    // jsonKeys.add(entry.getKey());
                    jsonKeys.add(parentStr.length()>0?parentStr+"/"+entry.getKey():entry.getKey());
                }
                getJsonKeys(entry.getValue(),jsonKeys, parentStr.length()==0?entry.getKey(): parentStr+"/"+entry.getKey());
            }
        }

        if (node.isArray()) {
            Iterator<JsonNode> it = node.iterator();
            while (it.hasNext()) {
                getJsonKeys(it.next(),jsonKeys, parentStr+"/*");
            }
        }
    }

    /**
     * 组装http接口地址: agentService + apiPath
     * @param api
     * @return
     */
    public static String makeHttpUrl(String api) {
        return makeUrl(StartConfig.getParamValueFromKey(StartConfig.agentService), api);
    }

    /**
     * 组装grpc接口地址
     * @param api
     * @return
     */
    public static String makeGrpcUrl(String api) {
        return makeUrl(StartConfig.getCommonParams().get("grpcserver"), api);
    }

    /**
     * url拼接,将不带具体服务端地址的api路径和服务端地址拼接成完整url
     * @param prefixUrl
     * @param api
     * @return
     */
    public static String makeUrl(String prefixUrl, String api) {
        if (isBlank(prefixUrl) || isBlank(api)) {
            return api;
        }
        if (api.startsWith(prefixUrl)) {
            return api;
        }
        if (prefixUrl.endsWith("/")) {
            return api.startsWith("/") ? prefixUrl + api.substring(1) : prefixUrl + api;
        } else {
            return api.startsWith("/") ? prefixUrl + api : prefixUrl + "/" + api;
        }
    }

}
